﻿using Microsoft.Extensions.Logging;

using PLCS.Application.Contracts.TaskApp;
using PLCS.Application.Contracts.TaskApp.Dtos;
using PLCS.Domain.Shared.Stations;
using PLCS.Domain.Shared.Tasks;
using PLCS.Domain.Stations;
using PLCS.Domain.Tasks;
using PLCS.Domain.Trays;
using PLCS.EquipmentService.ParsersHelpers;

using System.Collections.Concurrent;

using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;

namespace PLCS.Application.TaskApp;

public class TaskAppService : CrudAppService<TaskModel, TaskDto, Guid, PagedResultRequestDto, TaskEditDto>, ITaskAppService
{
    private readonly IHisTaskRepository _hisTaskRepository;
    private readonly ITaskRepository _taskRepository;
    private readonly ILogger<TaskAppService> _logger;
    private readonly TaskManager _taskManager;
    private readonly IRepository<Instruction, Guid> _instructionRepository;
    private readonly IRepository<Tray, Guid> _trayRepository;
    private readonly IStationRepository _stationRepository;
    private readonly PathAnalyzer _dataAnalyzer;

    public TaskAppService(IHisTaskRepository hisTaskRepository,
                       ITaskRepository taskRepository,
                       ILogger<TaskAppService> logger,
                       TaskManager taskManager,
                       IRepository<Instruction, Guid> instructionRepository,
                       IRepository<Tray, Guid> trayRepository,
                       IStationRepository stationRepository,
                       PathAnalyzer dataAnalyzer) : base(taskRepository)
    {
        _hisTaskRepository = hisTaskRepository;
        _taskRepository = taskRepository;
        _logger = logger;
        _taskManager = taskManager;
        _instructionRepository = instructionRepository;
        _trayRepository = trayRepository;
        _stationRepository = stationRepository;
        _dataAnalyzer = dataAnalyzer;
    }

    /// <summary>
    /// 创建任务
    /// </summary>
    /// <param name="taskInputDto"></param>
    /// <returns></returns>

    public override async Task<TaskDto> CreateAsync(TaskEditDto taskInputDto)
    {
        taskInputDto.TaskSource = "手动创建任务";

        var startStation = await _stationRepository.GetStationByNumAsync(taskInputDto.StartPos);
        if (startStation == null && taskInputDto.TaskType == TaskType.普通流转)
            throw new ArgumentException("起始工位不存在");
        var endStation = await _stationRepository.GetStationByNumAsync(taskInputDto.EndPos);
        if (endStation == null && taskInputDto.TaskType == TaskType.普通流转)
            throw new ArgumentException("目标工位不存在");

        if (await _taskRepository.CountAsync(x => x.StartPos == taskInputDto.StartPos) != 0)
            throw new ArgumentException("起始工位已存在任务");
        if (!string.IsNullOrEmpty(taskInputDto.TrayNo) && startStation != null)
        {
            var tray = await _trayRepository.FindAsync(x => x.TrayNo == taskInputDto.TrayNo);

            // 如果起始工位没有托盘，且该工位的光电状态显示有托盘
            if (startStation.GetTray() == null && startStation.HaveStatus == YesNo.是)
            {
                // 如果该托盘已存在，则判断该托盘所在的工位是否为空
                if (tray != null)
                {
                    if (tray.Station == null)
                    {
                        // 将托盘放入起始工位，并将起始工位更新到数据库
                        startStation.AddTray(tray);
                        await _stationRepository.UpdateAsync(startStation);
                    }
                    else
                    {
                        // 如果托盘已经在其他工位上，则抛出错误信息
                        throw new ArgumentException($"'{tray.TrayNo}'在'{tray.Station.StaNum}'上,请确认");
                    }
                }
                else
                {
                    // 如果该托盘不存在，则创建一个新的托盘对象，并将其放入起始工位
                    tray = new Tray(taskInputDto.TrayNo, taskInputDto.TrayType);
                    await _trayRepository.InsertAsync(tray);
                    startStation.AddTray(tray);
                    await _stationRepository.UpdateAsync(startStation);
                }
            }
            // 如果起始工位没有托盘，且该工位光电状态为空位
            else if (startStation.GetTray() == null && startStation.HaveStatus == YesNo.否)
            {
                // 抛出错误信息，提示该工位为空位
                throw new ArgumentException($"'{startStation.StaNum}'空位");
            }
            else
            {
                // 如果起始工位已有托盘，则判断该托盘的状态是否为“空闲”，并检查该托盘是否在起始工位中

                if (startStation.Trays == null || !startStation.Trays.Contains(tray))
                {
                    // 如果该托盘和起始起始工位的托盘不一致，则抛出错误信息
                    throw new ArgumentException($"'{taskInputDto.TrayNo}托盘号和当前工位{startStation.StaNum}的托盘号不一致");
                }
                await _trayRepository.UpdateAsync(tray);
            }
        }

        var entity = ObjectMapper.Map<TaskEditDto, TaskModel>(taskInputDto);
        await _taskManager.InsertAsync(entity);
        return await MapToGetOutputDtoAsync(entity);
    }

    /// <summary>
    /// 下发任务
    /// </summary>
    /// <param name="taskInputDto"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentException"></exception>
    public async Task SendTaskAsync(TaskEditDto taskInputDto)
    {
        taskInputDto.TaskSource = "首页下发任务";
        // 获取起始工位对象
        var startStation = await _stationRepository.GetStationByNumAsync(taskInputDto.StartPos) ?? throw new ArgumentException("起始工位不存在");
        // 获取目标工位对象
        var endStation = await _stationRepository.GetStationByNumAsync(taskInputDto.EndPos) ?? throw new ArgumentException("目标工位不存在");
        // 根据托盘编号获取托盘对象
        var tray = await _trayRepository.FindAsync(x => x.TrayNo == taskInputDto.TrayNo);

        // 如果起始工位没有托盘，且该工位的光电状态显示有托盘
        if (startStation.GetTray() == null && startStation.HaveStatus == YesNo.是)
        {
            // 如果该托盘已存在，则判断该托盘所在的工位是否为空
            if (tray != null)
            {
                if (tray.Station == null)
                {
                    // 将托盘放入起始工位，并将起始工位更新到数据库
                    startStation.AddTray(tray);
                    await _stationRepository.UpdateAsync(startStation);
                }
                else
                {
                    // 如果托盘已经在其他工位上，则抛出错误信息
                    throw new ArgumentException($"'{tray.TrayNo}'在'{tray.Station.StaNum}'上,请确认");
                }
            }
            else
            {
                // 如果该托盘不存在，则创建一个新的托盘对象，并将其放入起始工位
                tray = new Tray(taskInputDto.TrayNo, TrayType.未知);
                await _trayRepository.InsertAsync(tray);
                startStation.AddTray(tray);
                await _stationRepository.UpdateAsync(startStation);
            }
        }
        // 如果起始工位没有托盘，且该工位光电状态为空位
        else if (startStation.GetTray() == null && startStation.HaveStatus == YesNo.否)
        {
            // 抛出错误信息，提示该工位为空位
            throw new ArgumentException($"''{startStation.StaNum}'空位");
        }
        else
        {
            if (startStation.Trays == null || !startStation.Trays.Contains(tray))
            {
                // 如果该托盘和起始起始工位的托盘不一致，则抛出错误信息
                throw new ArgumentException($"'{taskInputDto.TrayNo}'不在工位'{startStation.StaNum}'中");
            }
            await _trayRepository.UpdateAsync(tray);
        }
        // 将任务信息转换为任务对象，并将其添加到数据库
        var entity = ObjectMapper.Map<TaskEditDto, TaskModel>(taskInputDto);
        await _taskManager.InsertAsync(entity);
    }

    /// <summary>
    /// 根据ID删除任务
    /// </summary>
    /// <param name="Id"></param>
    /// <returns></returns>
    ///
    public override async Task DeleteAsync(Guid id)
    {
        await _taskRepository.HardDeleteAsync(id);
    }

    /// <summary>
    /// 更新任务
    /// </summary>
    /// <param name="taskInputDto"></param>
    /// <returns></returns>
    public async Task UpdateAsync(TaskEditDto taskInputDto)
    {
        var entity = await GetEntityByIdAsync(taskInputDto.Id);
        await MapToEntityAsync(taskInputDto, entity);
        await Repository.UpdateAsync(entity, autoSave: true);
    }

    /// <summary>
    /// 通过任务ID获取任务
    /// </summary>
    /// <param name="taskId"></param>
    /// <returns></returns>
    public async Task<TaskDto> GetByTaskIdAsync(int taskId)
    {
        var entity = await _taskRepository.GetByTaskIdAsync(taskId);
        Check.NotNull(entity, taskId.ToString(), $"任务‘{taskId}’不存在");
        return ObjectMapper.Map<TaskModel, TaskDto>(entity);
    }

    /// <summary>
    /// 重新下发
    /// </summary>
    /// <param name="Id"></param>
    /// <param name="taskStatus"></param>
    /// <returns></returns>
    public async Task AgainIssuedAsync(Guid Id)
    {
        await _taskRepository.ChangeStatusAsync(Id, Domain.Shared.Tasks.TaskStatus.未执行);
    }

    /// <summary>
    /// 完成任务
    /// </summary>
    /// <param name="Id"></param>
    /// <returns></returns>
    public async Task FinshTaskAsync(Guid Id)
    {
        var instructions = await _instructionRepository.GetListAsync(x => x.TaskId == Id);
        if (instructions != null)
        {
            foreach (var item in instructions.OrderBy(x => x.InstructionNo))
            {
                item.ChangeStatus(Domain.Shared.Tasks.InstructionStatus.已完成);
            }
            await _instructionRepository.UpdateManyAsync(instructions);
        }
    }

    public async Task<PagedResultDto<TaskDto>> GetListBySearchAsync(PagedResultRequestDto input, string? taskNo, string? taskType, string? status, string? trayNo)
    {
        await CheckGetListPolicyAsync();

        var query = await ReadOnlyRepository.WithDetailsAsync(x => x.Instructions.OrderBy(x => x.InstructionNo));

        bool isInstructionNo = int.TryParse(taskNo, out int inttaskIdNo);
        query = query.WhereIf(isInstructionNo, x => x.TaskId == inttaskIdNo)
            .WhereIf(!string.IsNullOrEmpty(taskType), x => x.TaskType == (TaskType)Enum.Parse(typeof(TaskType), taskType))
            .WhereIf(!string.IsNullOrEmpty(status), x => x.TaskStatus == (Domain.Shared.Tasks.TaskStatus)Enum.Parse(typeof(Domain.Shared.Tasks.TaskStatus), status))
            .WhereIf(!string.IsNullOrEmpty(trayNo), x => x.TrayNo == trayNo)
            .OrderByDescending(x => x.CreationTime);
        var totalCount = await AsyncExecuter.CountAsync(query);

        query = ApplySorting(query, input);
        query = ApplyPaging(query, input);

        var entities = await AsyncExecuter.ToListAsync(query);
        var entityDtos = await MapToGetListOutputDtosAsync(entities);

        return new PagedResultDto<TaskDto>(
            totalCount,
            entityDtos
        );
    }
}